#include "LoginModule.h"
#include "CryptoSystem.h"
#include "Config.h"

#define LoginModule_HashSalt "-p0a{0}{1}a0p-#"

const char LoginModule_Query_UserDetails[] = "select * from `auth_data` where username='{0}';";

DatabaseConnectionInfo ConnData;

LoginModule::LoginModule()
{
	Status = LoginModule_NotConnected;
	DBHandle = dbMan.NewDatabaseInstance();
	ConnData.Host = conf.GetValue("login.database.host");
	ConnData.User = conf.GetValue("login.database.user");
	ConnData.Pass = conf.GetValue("login.database.pass");
	ConnData.DB = conf.GetValue("login.database.db");
	if (!(*DBHandle)->Open(*ConnData))
	{
		DBHandle.Release(); // remove the database handle
		printf("LoginModule: Could not open database connection\n");
		printf("Host: %s\nUser: %s\nPass: %s\nDB: %s\n", ConnData.Host.c_str(), ConnData.User.c_str(), ConnData.Pass.c_str(), ConnData.DB.c_str());
	}
}

NetworkMessage LoginModule::NewSession(NetworkMessage* msg)
{
	DBQuery strFmt;
	std::string user, pass, hash;
	NetworkMessage ret;
	Status = LoginModule_Connected;
	user = msg->GetString();
	pass = msg->GetString();
	if (user == "" || pass == "")
	{
		ret.WriteByte(LoginModule_AuthDetails);
		return ret;
	}
	strFmt.SetFormatString(LoginModule_HashSalt);
	strFmt.BindData(0, user);
	strFmt.BindData(1, pass);
	hash = crypto.Hash(strFmt.GetQuery()); // build the formatted system
	strFmt.Reset();
	strFmt.SetFormatString(LoginModule_Query_UserDetails);
	strFmt.BindData(0, user);
	if (DBHandle.IsEmpty())
	{
		ret.WriteByte(0xFE); // Database error: not open
		return ret;
	}
	IDBQueryResult* res = (*DBHandle)->Query(strFmt);
	if (!res) // does not have results
	{
		ret.WriteByte(0);
		return ret;
	}
	if (res->GetString("hashdata") != hash)
	{
		ret.WriteByte(0);
		return ret;
	}
	Status = LoginModule_Authenticated;
	User = user;
	AuthDetails = hash;
	Clearance = res->GetDoubleWord("clearance");
	delete res;
	ret.WriteByte(1);
	return ret;
}

NetworkMessage LoginModule::AuthDetails(NetworkMessage* msg)
{
	NetworkMesage ret;
	if (!msg || (Status & LoginModule_Connected) == 0)
	{
		ret.WriteByte(0xFF); // 0: user/pass not valid, 1: user/pass is valid, (1,0xFF]: error
		return ret;
	}
	user = msg->GetString();
	pass = msg->GetString();
	if (user == "" || pass == "")
	{
		ret.WriteByte(LoginModule_AuthDetails);
		return ret;
	}
	strFmt.SetFormatString(LoginModule_HashSalt);
	strFmt.BindData(0, user);
	strFmt.BindData(1, pass);
	hash = crypto.Hash(strFmt.GetQuery()); // build the formatted system
	strFmt.Reset();
	strFmt.SetFormatString(LoginModule_Query_UserDetails);
	strFmt.BindData(0, user);
	if (DBHandle.IsEmpty())
	{
		ret.WriteByte(0xFE); // Database error: not open
		return ret;
	}
	IDBQueryResult* res = (*DBHandle)->Query(strFmt);
	if (!res) // does not have results
	{
		ret.WriteByte(0);
		return ret;
	}
	if (res->GetString("hashdata") != hash)
	{
		ret.WriteByte(0);
		return ret;
	}
	Status = LoginModule_Authenticated;
	User = user;
	AuthDetails = hash;
	Clearance = res->GetDoubleWord("clearance");
	delete res;
	ret.WriteByte(1);
	return ret;
}

NetworkMessage LoginModule::CloseSession(NetworkMessage* msg)
{
	NetworkMessage ret;
	Conn->Close();
	Status = LoginModule_CloseSession;
	return ret;
}

NetworkMessage LoginModule::OnEvent(unsigned long event, NetworkMessage* msg)
{
	NetworkMessage resp;
	if (!msg)
	{
		return resp;
	}
	if (event == LoginModule_NewSession && ((Status & LoginModule_Connected) == 0)
	{
		return NewSession(msg);
	}
	else if (event == LoginModule_AuthDetails && ((Status & LoginModule_Authenticated) == 0)
	{
		return AuthDetails(msg);
	}
	else if (event == LoginModule_CloseSession && ((Status & LoginModule_NotConnected) == 0)
	{
		return CloseSession(msg);
	}
	else if (event == LoginModule_GetClearance && ((Status & LoginModule_Authenticated) != 0)
	{
		resp.Write4Bytes(Clearance); // no need to have its own function
		return resp;
	}
	return resp;
}
